/*
 *  ======== bitPosition ========
 *  return 1(0) if the specified bit position in value is set(clear)
 */
function bitPosition(value, position)
{
    if ((value & (1 << position)) != 0) {
        return (1);
    }
    return (0);
}

/*
 *  ======== eusci_calcBaudDividers ========
 *  computes the eUSCI_UART register settings for a given clock and baud rate
 *
 *  This function returns a JavaScript object containing the fields:
 *      UCOS16:      the oversampling bit (0 or 1)
 *      UCBRx:       the Baud Rate Control Word
 *      UCFx:        the First modulation stage select (UCBRFx)
 *      UCSx:        the Second modulation stage select (UCBRSx)
 *      maxAbsError: the maximum TX error for the register setting above
 *
 *  The first four field names match the names used in Table 18-5,
 *  "Recommended Settings for Typical Crystals and Baudrates", of the
 *  MSP430FR57xx Family User's Guide (SLAU272A).
 */
function eusci_calcBaudDividers(clockRate, baudRate)
{
    var maxAbsErrorInByte;
    var minAbsError;
    var error;
    var baudPeriod;
    var clockPeriod;
    var jj;
    var ii;
    var N;
    var count;

    var result = {UCOS16: 0, UCBRx: 0, UCFx: 0, UCSx: 0, maxAbsError: 0};
    
    N = Math.floor(clockRate / baudRate);
    baudPeriod = 1 / baudRate;
    clockPeriod = 1 / clockRate;
    
    minAbsError = 100000;
    for (jj = 0; jj <= 255; jj++) {

        maxAbsErrorInByte = 0;
        count = 0;
        for (ii = 0; ii <= 10; ii++) {
        count += N + bitPosition(jj, 7 - (ii % 8));

            error = (ii + 1) * baudPeriod - count * clockPeriod;
            if (error < 0) {
                error = -1 * error;
            }

            if (error > maxAbsErrorInByte) {
                maxAbsErrorInByte = error;
            }
        }

        if (maxAbsErrorInByte < minAbsError) {
            minAbsError = maxAbsErrorInByte;
            result.UCSx = jj;
        }
    }
    
    if (N < 20) {
        result.UCOS16 = 0;
        result.UCBRx  = N;
        result.UCFx   = 0;
    }
    else {
        result.UCOS16 = 1;
        result.UCBRx  = Math.floor(N / 16);
        result.UCFx   = N - (result.UCBRx * 16);
    }
    
    result.maxAbsError = minAbsError * baudRate * 100;

    return (result);
}

/*
 *  ======== USCI UART Baudrate functions========
 */
var localBRDIV;
var localUCxBR0UART;
var localUCxBR1UART;
var localUCxBR0SPI;
var localUCxBR1SPI;
var localUCxBRF;
var localUCxBRS;
var localBRCLK;

var NO_BITS = 1+8+1+1;

// Enable debugging
var VERBOSE = false;

// Calculate Standard (User's Guide) Settings with Errors
var STD_CALC = false;

// Find optimal setting for smallest receive error (otherwise smallest transmit error):
var OPT_FOR_RX = false;

// Software fix bug - What bug??
var SX_BUG = false;

var baudrate;
var brclk;

var lf_div_f;
var osr_div_f;
var lf_div_i, osr_div_i;

var UCSx_mod = 
      [[0,    0,    0,    0,    0,    0,    0,    0],
       [0,    1,    0,    0,    0,    0,    0,    0],
       [0,    1,    0,    0,    0,    1,    0,    0],
       [0,    1,    0,    1,    0,    1,    0,    0],
       [0,    1,    0,    1,    0,    1,    0,    1],
       [0,    1,    1,    1,    0,    1,    0,    1],
       [0,    1,    1,    1,    0,    1,    1,    1],
       [0,    1,    1,    1,    1,    1,    1,    1]];
       
function _Tbit_TX(mode, i, s_mod, f_mod)
{
    switch (mode)
    {
        case 0: // Ideal
            return(1/baudrate);
        case 1: // Low Frequency Baudrate Generation
            return((1/brclk)*(lf_div_i + UCSx_mod[s_mod % 8][i % 8]));
        case 2: // Oversampling Baudrate Generation
            if(SX_BUG) { // with Sx bug:
                return((1/brclk)*((16+UCSx_mod[s_mod % 8][i % 8] - (i > 0 ? UCSx_mod[s_mod % 8][(i-1) % 8]:0) )*osr_div_i + f_mod));
            }
            else { // No Sx bug:
                return((1/brclk)*((16+UCSx_mod[s_mod % 8][i % 8])*osr_div_i + f_mod));
            }
        default: // Ideal
            return(1/baudrate);
    }
} // Tbit_TX_ideal

function _TX_error(mode , s_mod , f_mod, return_max_error)
{
    var i = 0;
    var t_ideal= 0; 
    var t_usci= 0;
    var bit_error= 0;
    
    return_max_error.max_error = 0; return_max_error.max_neg_error = 0; return_max_error.max_pos_error = 0;
    
    for (i= 0; i < NO_BITS; i++) {
        t_ideal= t_ideal + _Tbit_TX(0, i, 0, 0);
        t_usci = t_usci  + _Tbit_TX(mode, i, s_mod, f_mod);
        bit_error= (t_usci - t_ideal) * baudrate * 100;
        if (bit_error < return_max_error.max_neg_error) {
            return_max_error.max_neg_error = bit_error;
        }
        if (bit_error > return_max_error.max_pos_error) {
            return_max_error.max_pos_error= bit_error;
        }
        if ((bit_error*bit_error) > ((return_max_error.max_error)*(return_max_error.max_error))) {
            return_max_error.max_error= bit_error;
        }
    } // for i
    
    return return_max_error;
} // _TX_error

function _RX_error(mode, s_mod, f_mod, t_sync, return_max_error)
{
    var i;
    var j;
    var half_bit_clocks;
    var t_ideal= 0; 
    var t_usci= 0;
    var bit_error= 0;
    
    return_max_error.max_error= 0; return_max_error.max_pos_error= 0; return_max_error.max_neg_error= 0;
    
    for (i= 0; i < NO_BITS; i++)
    {
        t_ideal= (i + 0.5) / baudrate;
        t_usci= 0;
        for (j= 0; j < i; j++)
        { t_usci = t_usci + _Tbit_TX(mode, j, s_mod, f_mod); }
        if (mode == 2) // Oversampling Baudrate Generation
        {
            if (SX_BUG) {
                if (f_mod == 15)
                { t_usci= t_usci + (1/brclk)*(t_sync + (8+UCSx_mod[s_mod%8][i%8] - (i > 0 ? UCSx_mod[s_mod % 8][(i-1) % 8]:0) )*osr_div_i + parseInt(7 + UCSx_mod[s_mod%8][i%8])); }
                else    
                { t_usci= t_usci + (1/brclk)*(t_sync + (8+UCSx_mod[s_mod%8][i%8] - (i > 0 ? UCSx_mod[s_mod % 8][(i-1) % 8]:0) )*osr_div_i + parseInt((f_mod+1)/2)); }
            }
            else { // not defined SX_BUG                
                if (f_mod == 15)
                { t_usci= t_usci + (1/brclk)*(t_sync + (8+UCSx_mod[s_mod%8][i%8])*osr_div_i + parseInt(7 + UCSx_mod[s_mod%8][i%8])); }
                else    
                { t_usci= t_usci + (1/brclk)*(t_sync + (8+UCSx_mod[s_mod%8][i%8])*osr_div_i + parseInt((f_mod+1)/2)); }
            } // SX_BUG
        }
        else // Low Frequency Baudrate Generation
        { t_usci= t_usci + (1/brclk)*(t_sync + parseInt(lf_div_i/2) + UCSx_mod[s_mod%8][i%8]); }
          
        bit_error= (t_usci - t_ideal) * baudrate * 100;

        if (bit_error < return_max_error.max_neg_error)
        { return_max_error.max_neg_error= bit_error; }
        if (bit_error > return_max_error.max_pos_error)
        { return_max_error.max_pos_error= bit_error; }
        if ((bit_error*bit_error) > ((return_max_error.max_error)*(return_max_error.max_error)))
        { return_max_error.max_error= bit_error; }
    } // for i
    
    return return_max_error;
} // _RX_error


function _RX_error_worst_case(mode, s_mod, f_mod, worst_case_error)
{
 //   var max_error, max_pos_error, max_neg_error;
    var max_worse_case_error = {
                    max_error : 0,
                    max_pos_error : 0,
                    max_neg_error : 0
                    };
                    
    _RX_error(mode, s_mod, f_mod, -0.5, max_worse_case_error);
    worst_case_error= max_worse_case_error.max_error;
    _RX_error(mode, s_mod, f_mod,  0.0, max_worse_case_error);
    if ((worst_case_error)*(worst_case_error) < max_worse_case_error.max_error*max_worse_case_error.max_error) {
      worst_case_error= max_worse_case_error.max_error;
    }
    _RX_error(mode, s_mod, f_mod, +0.5, max_worse_case_error);
    if ((worst_case_error)*(worst_case_error) < max_worse_case_error.max_error*max_worse_case_error.max_error) {
      worst_case_error= max_worse_case_error.max_error;
    }
    
    return worst_case_error;
} // _RX_error_worst_case

 /*
 *  Calculate baud dividers based on target baudrates
 */
function usci_calcBaudDividers(brclk_f , baudrate_f)
{
    var s_mod, f_mod;
    var opt_s_mod = 0, opt_f_mod = 0;
    var os_opt_s_mod = 0, os_opt_f_mod = 0;
    var max_error = {
                    max_error : 0,
                    max_pos_error : 0,
                    max_neg_error : 0
                    };

    var abs_max = { 
                    max_error : 0,
                    max_pos_error : 0,
                    max_neg_error : 0
                    };

    var result = {UCOS16: 0, UCBRx: 0, UCFx: 0, UCSx: 0, maxAbsError: 0};

    if ((baudrate_f==0) && (brclk_f==0)) {
       return;
    }
    else {
        baudrate = baudrate_f;
        brclk = brclk_f;
    }
    
    // brclk= brclk * 1E6;        // Convert MHz   -> Hz
    // baudrate= baudrate * 1E3;  // Convert kBaud -> Baud

    //Starting calculation with low frequency
    lf_div_f= (brclk)/(baudrate);
    lf_div_i= parseInt(lf_div_f);

    if (STD_CALC) {
        opt_s_mod= Math.round((lf_div_f-lf_div_i)*8);
        _TX_error(1, opt_s_mod, 0, abs_max);
    }
    else { // not defined STD_CALC
        opt_s_mod= -1; 
        abs_max.max_error= abs_max.max_pos_error= abs_max.max_neg_error= 0;
    
        for (s_mod= 0; s_mod < 8; s_mod++)
        {
            if (OPT_FOR_RX) { // Optimize for minimal receive error
                _RX_error_worst_case(1, s_mod, 0, max_error.max_error);
            }
            else { // not defined OPT_FOR_RX
                _TX_error(1, s_mod, 0, max_error);
            }
            
            if (((max_error.max_error*max_error.max_error) < (abs_max.max_error*abs_max.max_error)) ||
                (opt_s_mod == -1))
            {
                abs_max.max_error= max_error.max_error;
                abs_max.max_pos_error= max_error.max_pos_error;
                abs_max.max_neg_error= max_error.max_neg_error;
                opt_s_mod= s_mod;
            }
        } // for s_mod
        
    } // STD_CALC

    _TX_error(1, opt_s_mod, 0, max_error);

    result.UCOS16 = 0;
    result.UCBRx = lf_div_i;
    result.UCFx = opt_f_mod;
    result.UCSx = opt_s_mod;
    result.maxAbsError = max_error.max_error;
    /*
    if (VERBOSE) {
        print("Divider = " + lf_div_f + ";  BRDIV = " + lf_div_i);
        print("S-Modulation UCBRSx = " + opt_s_mod);
        print("Max. TX bit error: " + max_error.max_error + "(" + max_error.max_neg_error + ", " + max_error.max_pos_error + ")");
    }
    else {
        print(lf_div_i + "\t" + opt_s_mod + "\t" + max_neg_error + "\t" + max_error.max_pos_error + "\t");
    }

    abs_max.max_error= abs_max.max_pos_error= abs_max.max_neg_error= 0;
    _RX_error(1, opt_s_mod, 0, -0.5, max_error);
    if (max_error.max_error > abs_max.max_error) { abs_max.max_error= max_error.max_error; }
    if (max_error.max_pos_error > abs_max.max_pos_error) { abs_max.max_pos_error= max_error.max_pos_error; }
    if (max_error.max_neg_error < abs_max.max_neg_error) { abs_max.max_neg_error= max_error.max_neg_error; }
    if (VERBOSE) {
        print("Max. RX bit error (sync.: -0.5 BRCLK): " + max_error.max_error + "(" + max_error.max_neg_error + "," + max_error.max_pos_error + ")");
    }
    else {
        print(max_error.max_neg_error + "\t" + max_error.max_pos_error + "\t");
    }
    _RX_error(1, opt_s_mod, 0, 0, max_error);
    if (max_error.max_error > abs_max.max_error) { abs_max.max_error= max_error.max_error; }
    if (max_error.max_pos_error > abs_max.max_pos_error) { abs_max.max_pos_error= max_error.max_pos_error; }
    if (max_error.max_neg_error < abs_max.max_neg_error) { abs_max.max_neg_error= max_error.max_neg_error; }
    if (VERBOSE) {
        print("Max. RX bit error (sync.: +/-0 BRCLK): " + max_error.max_error + "(" + max_error.max_neg_error + "," + max_error.max_pos_error + ")");
    }
    else {
        print(max_error.max_neg_error + "\t" + max_error.max_pos_error + "\t");
    }
    _RX_error(1, opt_s_mod, 0, +0.5, max_error);
    if (max_error.max_error > abs_max.max_error) { abs_max.max_error= max_error.max_error; }
    if (max_error.max_pos_error > abs_max.max_pos_error) { abs_max.max_pos_error= max_error.max_pos_error; }
    if (max_error.max_neg_error < abs_max.max_neg_error) { abs_max.max_neg_error= max_error.max_neg_error; }
    if (VERBOSE) {
        print("Max. RX bit error (sync.: +0.5 BRCLK): " + max_error.max_error + "(" + max_error.max_neg_error + "," + max_error.max_pos_error + ")");
        print("Max. RX bit error (over: -0.5,0,+0.5): " + abs_max.max_error + "(" + abs_max.max_neg_error + "," + abs_max.max_pos_error + ")");
    }
    else {
        print(max_error.max_neg_error + "\t" + max_error.max_pos_error + "\t");
        print(abs_max.max_neg_error + "\t" + abs_max.max_pos_error + "\t");
    }
    */

    //Starting calculation with oversampling
    if (lf_div_f >= 16)
    {
        osr_div_f= (brclk)/(baudrate)/16;
        osr_div_i= parseInt(osr_div_f);

        if (STD_CALC) {
            if (osr_div_i <= 1)
            {   os_opt_f_mod= 0; // The FX modulation does not work with a BR divider <=1 because the prescaler is bypassed in this case.
            } 
            else
            {   os_opt_f_mod= parseInt((osr_div_f-osr_div_i)*16);
            } // else
            os_opt_s_mod= 0;
            _TX_error(2, os_opt_s_mod, os_opt_f_mod, abs_max);
        }
        else { // not defined STD_CALC
            os_opt_s_mod= -1;
            abs_max.max_error= abs_max_pos_error= abs_max.max_neg_error= 0;
    
            for (f_mod=0; f_mod < (osr_div_i <= 1 ? 1 : 16); f_mod++)
            // Loop length depends on "osr_div_i" because FX modulation does not work with a BR divider <=1 because the prescaler is bypassed in this case.
            // osr_div_i<=1 => only f_mod = 0 is valid
            // osr_div_i >1 => f_mod can vary between 0 and 15.
            {
                for (s_mod= 0; s_mod < 8; s_mod++)
                {
                    if (OPT_FOR_RX) { // Optimize for minimal receive error
                        _RX_error_worst_case(2, s_mod, f_mod, max_error.max_error);
                    } 
                    else { // not defined OPT_FOR_RX
                        _TX_error(2, s_mod, f_mod, max_error);
                    }
                    if (((max_error.max_error*max_error.max_error) < (abs_max.max_error*abs_max.max_error)) ||
                        (os_opt_s_mod == -1))
                    {
                       abs_max.max_error= max_error.max_error;
                       abs_max.max_pos_error= max_error.max_pos_error;
                       abs_max.max_neg_error= max_error.max_neg_error;
                       os_opt_s_mod= s_mod;
                       os_opt_f_mod= f_mod;
                    }
                } // for s_mod
            } // for f_mod
        } // STD_CALC    

        if (osr_div_i==1) {
            osr_div_i= 2;
            _TX_error(2, 0, 0, max_error);
            if ((max_error.max_error*max_error.max_error) < (abs_max.max_error*abs_max.max_error)) {
                abs_max.max_error= max_error.max_error;
                abs_max.max_pos_error= max_error.max_pos_error;
                abs_max.max_neg_error= max_error.max_neg_error;
                os_opt_s_mod= 0;
                os_opt_f_mod= 0;
            }
            else { 
                osr_div_i= 1;
            }
        }
        
        _TX_error(2, os_opt_s_mod, os_opt_f_mod, max_error);
        
        if ((max_error.max_error*max_error.max_error) <= (50*50)) {

            result.UCOS16 = 1;
            result.UCBRx = osr_div_i;
            result.UCFx = os_opt_f_mod;
            result.UCSx = os_opt_s_mod;
            result.maxAbsError = max_error.max_error;
        }

        /*
        if ((max_error*max_error) > (50*50)) {
            if (VERBOSE) {
                print("... not feasible! Max. error > 50%\n");
            }
            else {
                print("Not feasible! Max. error > 50%\n");
            }
        }
        else
        {
            if (VERBOSE) {
                print("Divider= " + osr_div_f + ";  BRDIV= " + osr_div_i);
                print("S-Modulation UCBRSx= " + opt_s_mod);
                print("F-Modulation UCBRFx= " + opt_f_mod);
                print("Max. TX bit error: " + max_error.max_error + "(" + max_error.max_neg_error + "," + max_error.max_pos_error + ")");
            }
            else {
                print(osr_div_i + "\t" + opt_s_mod + "\t" + opt_f_mod + "\t" + max_error.max_neg_error + "\t" + max_error.max_pos_error + "\t");
            }

            abs_max.max_error= abs_max.max_pos_error= abs_max.max_neg_error= 0;
            _RX_error(2, opt_s_mod, opt_f_mod, -0.5, max_error);
            if (max_error.max_error > abs_max.max_error) { abs_max.max_error= max_error.max_error; }
            if (max_error.max_pos_error > abs_max.max_pos_error) { abs_max.max_pos_error= max_error.max_pos_error; }
            if (max_error.max_neg_error < abs_max.max_neg_error) { abs_max.max_neg_error= max_error.max_neg_error; }
            if (VERBOSE) {
                print("Max. RX bit error (sync.: -0.5 BRCLK): " + max_error.max_error + "(" + max_error.max_neg_error + "," + max_error.max_pos_error + ")");
            }
            else {
                print("%f\t%f\t", max_error.max_neg_error, max_error.max_pos_error);
            }
            _RX_error(2, opt_s_mod, opt_f_mod, 0, max_error);
            if (max_error.max_error > abs_max.max_error) { abs_max.max_error= max_error.max_error; }
            if (max_error.max_pos_error > abs_max.max_pos_error) { abs_max.max_pos_error= max_error.max_pos_error; }
            if (max_error.max_neg_error < abs_max.max_neg_error) { abs_max.max_neg_error= max_error.max_neg_error; }
            if (VERBOSE) {
                print("Max. RX bit error (sync.: +/-0 BRCLK): " + max_error.max_error + "(" + max_error.max_neg_error + "," + max_error.max_pos_error + ")");
            }
            else {
                print("%f\t%f\t", max_neg_error, max_pos_error);
            }
            _RX_error(2, opt_s_mod, opt_f_mod, +0.5, max_error);
            if (max_error.max_error > abs_max.max_error) { abs_max.max_error= max_error.max_error; }
            if (max_error.max_pos_error > abs_max.max_pos_error) { abs_max.max_pos_error= max_error.max_pos_error; }
            if (max_error.max_neg_error < abs_max.max_neg_error) { abs_max.max_neg_error= max_error.max_neg_error; }
            if (VERBOSE) {
                print("Max. RX bit error (sync.: +0.5 BRCLK): " + max_error.max_error + "(" + max_error.max_neg_error + "," + max_error.max_pos_error + ")");
                print("Max. RX bit error (over: -0.5,0,+0.5): " + abs_max.max_error + "(" + abs_max.max_neg_error + "," + abs_max.max_pos_error + ")");
            } 
            else {
                print(max_error.max_neg_error + "\t" + max_error.max_pos_error + "\t");
                print(abs_max.max_neg_error + "\t" + abs_max.max_pos_error + "\t");
            }
        } //else
        */
    } // if

    return (result);
} // _CalculateBaudDividers


function calculateBaudrate() 
{
    var res = {UCOS16: 0, UCBRx: 0, UCFx: 0, UCSx: 0, maxAbsError: 0};
    var usciInput = document.getElementById('usci').value;
    var clkSel= document.getElementById('clocksel').value; 
    var baudSel = document.getElementById('brsel').value;
    var baudInput;
    var clkInput;

    if (clkSel == "Custom") {
        clkInput = document.getElementById('clock').value;
    }
    else {
        clkInput = clkSel;
    }

    if (baudSel == "Custom") {
        baudInput = document.getElementById('baud').value;
    }
    else {
        baudInput = baudSel;
    }

    //Pre-check if baud rate equals or larger than clock frequency
    if (parseInt(baudInput) >= parseInt(clkInput)) {
        res.UCBRx = "N/A";
        res.UCFx = "N/A";
        res.UCSx = "N/A";
        res.UCOS16 = "N/A";
    }
    else {
        if (usciInput == "EUSCI") {
            res = eusci_calcBaudDividers(clkInput, baudInput);
        }
        else {
            res = usci_calcBaudDividers(clkInput, baudInput);
        }
    }

    document.getElementById('res_API').innerHTML = 'The recommended parameters for DriverLib are:';

    document.getElementById('lab_UCBRx').innerHTML = 'clockPrescalar:';
    document.getElementById('lab_UCFx').innerHTML = 'firstModReg:';
    document.getElementById('lab_UCSx').innerHTML = 'secondModReg:';
    document.getElementById('lab_UCOS16').innerHTML = 'overSampling:';  

    document.getElementById('res_UCBRx').innerHTML = '<input type="text" size="5" readonly value=' + res.UCBRx + '></input>';
    document.getElementById('res_UCFx').innerHTML = '<input type="text" size="5" readonly value=' + res.UCFx + '></input>';
    document.getElementById('res_UCSx').innerHTML = '<input type="text" size="5" readonly value=' + res.UCSx + '></input>';
    document.getElementById('res_UCOS16').innerHTML = '<input type="text" size="5" readonly value=' + res.UCOS16 + '></input>';
}

function checkClockCustom()
{
    var clkSel = document.getElementById('clocksel').value;

    if (clkSel == "Custom") {
        document.getElementById('clockCell').innerHTML = '<input id= "clock" name="clock" type="text" size="10" value="32768">Hz</input>';
    }
    else {
        document.getElementById('clockCell').innerHTML = '';
    }
}

function checkBaudrateCustom()
{
    var baudSel = document.getElementById('brsel').value;

    if (baudSel == "Custom") {
        document.getElementById('baudrateCell').innerHTML = '<input id="baud" name="baud" type="text" size="10" value="9600">bps</input>';
    }
    else {
        document.getElementById('baudrateCell').innerHTML = '';
    }
}